package cn.edu.cdtu.yulei.test_demo.utils;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.*;

public class FileDownloader {
    private static final int NUM_THREADS = 2; // 并发下载的线程数

    /**
     * 下载文件
     * @param url 远程文件地址
     */
    public static void download(String url) {
        String savePath = "src/main/resources/static/file.txt"; // 下载文件保存路径
        int numChunks = 2; // 切片数量

        try {
            // 创建线程池
            ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);

            // 创建下载任务列表
            CompletionService<Boolean> completionService = new ExecutorCompletionService<>(executor);

            // 下载每个文件片段
            for (int i = 0; i < numChunks; i++) {
                int chunkIndex = i;
                completionService.submit(() -> downloadChunk(url, savePath, numChunks, chunkIndex));
            }

            // 等待所有任务完成
            for (int i = 0; i < numChunks; i++) {
                Future<Boolean> future = completionService.take();
                if (!future.get()) {
                    System.out.println("下载失败");
                    executor.shutdownNow();
                    return;
                }
            }

            // 合并文件片段
            if (mergeChunks(savePath, numChunks)) {
                System.out.println("下载完成");
            } else {
                System.out.println("合并文件失败");
            }

            // 关闭线程池
            executor.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 进行切片下载
     * @param fileUrl 远程文件路径
     * @param savePath 文件保存路径
     * @param numChunks 文件分片数
     * @param chunkIndex 分片索引
     * @return
     */
    private static boolean downloadChunk(String fileUrl, String savePath, int numChunks, int chunkIndex) {
        try {
            // 计算每个文件片段的下载范围
            long chunkSize = getFileSize(fileUrl) / numChunks;
            long startByte = chunkIndex * chunkSize;
            long endByte = (chunkIndex == numChunks - 1) ? -1 : (chunkIndex + 1) * chunkSize - 1;

            // 构建文件保存路径
            String chunkPath = savePath + ".part" + chunkIndex;
            // 下载文件片段
            URL url = new URL(fileUrl);
            try (BufferedInputStream inputStream = new BufferedInputStream(url.openStream());
                 RandomAccessFile outputStream = new RandomAccessFile(chunkPath, "rw")) {
                outputStream.seek(startByte);

                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                    if (endByte != -1 && outputStream.getFilePointer() >= endByte) {
                        break;
                    }
                }
            }

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 合并临时文件
     * @param savePath 文件保存路径
     * @param numChunks 文件分片数
     * @return
     */
    private static boolean mergeChunks(String savePath, int numChunks) {
        try {
            // 打开目标文件
            try (RandomAccessFile outputFile = new RandomAccessFile(savePath, "rw")) {
                // 合并每个文件片段
                for (int i = 0; i < numChunks; i++) {
                    String chunkPath = savePath + ".part" + i;
                    try (RandomAccessFile chunkFile = new RandomAccessFile(chunkPath, "r")) {
                        byte[] buffer = new byte[1024];
                        int bytesRead;
                        while ((bytesRead = chunkFile.read(buffer)) != -1) {
                            outputFile.write(buffer, 0, bytesRead);
                        }
                    }
                    // 删除文件片段
                    File chunk = new File(chunkPath);
                    chunk.delete();
                }
            }

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取文件大小
     * @param fileUrl 文件路径
     * @return
     * @throws IOException
     */
    private static long getFileSize(String fileUrl) throws IOException {
        URL url = new URL(fileUrl);
        URLConnection connection = url.openConnection();
        return connection.getContentLengthLong();
    }
}